Put a bird on it - Portlandia.
If you missed out on Portlandia, you should take some time to watch this clip of the “Put a bird on it” episode.
Just like Bryce and Lisa - we can put birds on anything with the magick package from ROpenSci!
So let’s get started putting birds on things!
The magick package is an R interface to the ImageMagick STL, one of the most comprehensive open source image processing libraries. It gives a lot of power to R users, and we’ll briefly cover the workflow before getting into our real example.
If you want a deeper dive on all the features of magick, check out their awesome vignette!
# Load the library
library(magick)
# "read" in an image
dog <- image_read("https://images.pexels.com/photos/1564506/pexels-photo-1564506.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940")
image_info(dog)
format width height colorspace matte filesize density
1 JPEG 1880 1208 sRGB FALSE 87114 72x72
We can see a few things about the r_logo object, it is a .png, it’s height and width, filesize, and DPI/density.
We can also view the image in line.
print(dog)
format width height colorspace matte filesize density
1 JPEG 1880 1208 sRGB FALSE 87114 72x72
We also have image manipulation options like image_rotate, image_resize, all of which are pipeable with the %>%.
dog %>%
image_rotate(30) %>% # rotate 30 degrees
image_resize("400x400") %>% # change size to 400 x 400 pixels
image_flop() # flip the image horizontally
Most importantly we can put a bird on it!
bird <- image_read("https://user-images.githubusercontent.com/29187501/38769895-edae85d0-3fcf-11e8-95f9-bbd530b32771.png") %>%
image_resize("150x150")
image_composite(dog, bird)
Now please note where the bird showed up, in the top left corner. We can control the placement of composite images by specifying the offset.
dog %>%
image_composite(bird, offset = "+500+1000")
Notice that offset reads as x-axis pixels, y-axis pixels, so "+500+1000 indicates from the top left corner place the image 500 pixels to the right and 1000 pixels down.
Now that we’ve been oriented to how the magick package works, and had some fun by putting a bird on our lovely Boston Terrier model, let’s dive into the real purpose of this post - adding a logo to your ggplot!
In the exampe I will show below you will have already saved the plot as an image, so we can skip with the normal ggplot2 code. However, please note that if you do want to build in some extra whitespace to your plot to save “space” for your logo you can do so by changing the margins of your overall plot. You can also add a figure caption with labs(caption = "text to change here"), which inherently adds some whitespace to incorporate the caption.
Margins can take multiple units arguments including: “cm”, “in”, “lines”, “pt”, for more info check out the ggplot2 docs.
Notice: The margins read as t, r, b, l or c(top, right, bottom, left). I typically see logos on the bottom left or top right so you can:
add space at the top for example with:
c(2.5, 0.5, 0.5, 0.5)
add space to the bottom with:
c(0.5, 0.5, 2.5, 0.5)
Notice the below plot has extra space on the bottom, and I have added a grey background purely to highlight this extra space.
library(tidyverse)
mtcars %>%
ggplot(aes(x = cyl, y = mpg)) +
geom_point() +
theme(plot.margin = unit(c(0.5, 0.5, 2.5, 0.5), "lines"),
plot.background = element_rect("lightgrey"))
# plot background = lightgrey for highlight, not necessary for sizing
So let’s read in our publication plot to work with! Please notice that this can be a local file with traditional path structure or a hosted image as a url. I’m using URLs so you can try these out yourself!
pub_plot <- image_read("https://raw.githubusercontent.com/jthomasmock/tomtom/master/vignettes/basic_plot.png")
logo <- image_read("https://www.rstudio.com/wp-content/uploads/2018/10/RStudio-Logo-Flat.png") %>%
image_resize(300)
print(pub_plot)
# A tibble: 1 x 7
format width height colorspace matte filesize density
<chr> <int> <int> <chr> <lgl> <int> <chr>
1 PNG 2222 1951 sRGB TRUE 168897 72x72
print(logo)
# A tibble: 1 x 7
format width height colorspace matte filesize density
<chr> <int> <int> <chr> <lgl> <int> <chr>
1 PNG 300 105 sRGB TRUE 0 57x57
We can then overlay the logo over the plot, let’s try bottom left, and we want ~ 1% padding for aesthetics - so we can use the following code to get dimensions and pixel numbers.
# get dims of the plot
plot_height <- magick::image_info(pub_plot)$height
plot_width <- magick::image_info(pub_plot)$width
# get dims of the logo
logo_width <- magick::image_info(logo)$width
logo_height <- magick::image_info(logo)$height
# get number of pixels to be 1% from the bottom of the plot
# while accounting for the logo height
plot_height - logo_height - plot_height * 0.01
[1] 1826.49
# get number of pixels to be 1% from the left of the plot
plot_width * 0.01
[1] 22.22
By using offset = "+22+1826" we indicate that we are placing the logo 22 pixels to the right and 1826 pixels down.
pub_plot %>%
image_composite(logo, offset = "+22+1826")
Boom! We have our logo overlay on the plot in the right location! But we had to manually figure out and set the logo position, which is less than ideal for programmatic use down the road. I also manually resized the logo to 300 pixels in an earlier step - we should make that automagick as well!
To make the logo add process more reproducible, we can build a function to take the various heights and widths of the logo/plot and automatically resize the logo to match as well as calculating the number of pixels to put it in each respective corner. The logo_scale argument defaults to 10 - so the logo will be 1/10th the width of the plot.
plot_with_logo <- add_logo(
plot_path = "", # url or local file for the plot
logo_path = "", # url or local file for the logo
logo_position = "" # choose a corner
# 'top left', 'top right', 'bottom left' or 'bottom right'
#logo_scale = 10 as default, but can change to manually make logo bigger
)
# save the image and write to working directory
magick::image_write(plot_with_logo, "plot_with_logo.png")
This function takes in two images, gets the dimension information, then pastes the logo at 1/10th scale in the specified corner with 1% padding. The function also has a warning if you input a logo_position that is not included.
add_logo <- function(plot_path, logo_path, logo_position, logo_scale = 10){
# Requires magick R Package https://github.com/ropensci/magick
# Useful error message for logo position
if (!logo_position %in% c("top right", "top left", "bottom right", "bottom left")) {
stop("Error Message: Uh oh! Logo Position not recognized\n Try: logo_positon = 'top left', 'top right', 'bottom left', or 'bottom right'")
}
# read in raw images
plot <- magick::image_read(plot_path)
logo_raw <- magick::image_read(logo_path)
# get dimensions of plot for scaling
plot_height <- magick::image_info(plot)$height
plot_width <- magick::image_info(plot)$width
# default scale to 1/10th width of plot
# Can change with logo_scale
logo <- magick::image_scale(logo_raw, as.character(plot_width/logo_scale))
# Get width of logo
logo_width <- magick::image_info(logo)$width
logo_height <- magick::image_info(logo)$height
# Set position of logo
# Position starts at 0,0 at top left
# Using 0.01 for 1% - aesthetic padding
if (logo_position == "top right") {
x_pos = plot_width - logo_width - 0.01 * plot_width
y_pos = 0.01 * plot_height
} else if (logo_position == "top left") {
x_pos = 0.01 * plot_width
y_pos = 0.01 * plot_height
} else if (logo_position == "bottom right") {
x_pos = plot_width - logo_width - 0.01 * plot_width
y_pos = plot_height - logo_height - 0.01 * plot_height
} else if (logo_position == "bottom left") {
x_pos = 0.01 * plot_width
y_pos = plot_height - logo_height - 0.01 * plot_height
}
# Compose the actual overlay
magick::image_composite(plot, logo, offset = paste0("+", x_pos, "+", y_pos))
}
So there you go! Happy logo adding and bird putting!
For more information on adding logos to plots with magick::image_append() instead, check out this blog post by Daniel Hadley on using magick::image_append to add a logo or his RStudioConf talk on the same idea.
Cheers!
For attribution, please cite this work as
Mock (2019, Jan. 9). The Mockup Blog: Add a logo to your plot. Retrieved from https://themockup.netlify.app/posts/2019-01-09-add-a-logo-to-your-plot/
BibTeX citation
@misc{mock2019add,
author = {Mock, Thomas},
title = {The Mockup Blog: Add a logo to your plot},
url = {https://themockup.netlify.app/posts/2019-01-09-add-a-logo-to-your-plot/},
year = {2019}
}